/*
 * Copyright 2020-2030 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gitee.minimalismstyle.fresh.oauth.provider

import com.gitee.minimalismstyle.fresh.oauth.service.FreshUserDetailsService
import com.gitee.minimalismstyle.fresh.sys.api.service.SysSmscodeService
import com.gitee.minimalismstyle.fresh.sys.api.service.SysUserService
import org.springframework.security.authentication.BadCredentialsException
import org.springframework.security.authentication.InternalAuthenticationServiceException
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
import org.springframework.security.core.AuthenticationException
import org.springframework.security.core.userdetails.UserDetails
import org.springframework.security.core.userdetails.UsernameNotFoundException
import org.springframework.security.crypto.factory.PasswordEncoderFactories
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.util.Assert

class AnyAuthenticationProvider : AbstractUserDetailsAuthenticationProvider() {
    var passwordEncoder: PasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder()

    val userNotFoundEncodedPassword: String? = null

    var userDetailsService: FreshUserDetailsService? = null

    var smscodeService: SysSmscodeService? = null

    var userService: SysUserService? = null

    @Throws(AuthenticationException::class)
    override fun additionalAuthenticationChecks(userDetails: UserDetails, authentication: UsernamePasswordAuthenticationToken) {
        if (authentication.credentials == null) {
            this.logger.debug("Authentication failed: no credentials provided")
            throw BadCredentialsException(this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"))
        } else {
            val name = authentication.name
            val type = getType(name)
            if (type == "username") {
                val presentedPassword = authentication.credentials.toString()
                if (!this.passwordEncoder.matches(presentedPassword, userDetails.password)) {
                    this.logger.debug("Authentication failed: password does not match stored value")
                    throw BadCredentialsException("用户名或者密码错误")
                }
            } else if (type == "phone") {
                val phone = name.replace("{$type}", "")
                val code = authentication.credentials.toString()
                if (smscodeService!!.validate(phone, code).get()) {
                    this.logger.debug("Authentication failed: password does not match stored value")
                    throw BadCredentialsException("验证码不存在或者已过期")
                }
            }


        }
    }

    private fun getType(name: String): String {
        return if (!name.startsWith("{")) {
            "username"
        } else {
            val end = name.indexOf("}")
            name.substring(1, end)
        }
    }

    @Throws(Exception::class)
    override fun doAfterPropertiesSet() {
        Assert.notNull(this.userDetailsService, "A UserDetailsService must be set")
    }

    override fun retrieveUser(username: String, authentication: UsernamePasswordAuthenticationToken): UserDetails? {
        try {
            var loadedUser: UserDetails? = null
            val name = authentication.name
            val type = getType(authentication.name)
            if (type == "username") {
                val userName = name.replace("{$type}", "")
                loadedUser = this.userDetailsService!!.loadUserByUsername(userName)
                if (loadedUser == null) {
                    loadedUser = this.userDetailsService!!.loadUserByPhone(userName)
                    if (loadedUser == null) {
                        throw InternalAuthenticationServiceException("用户名不存在")
                    }
                }
            } else if (type == "phone") {
                val phone = name.replace("{$type}", "")

                loadedUser = this.userDetailsService!!.loadUserByPhone(phone)

                if (loadedUser == null) {
                    throw InternalAuthenticationServiceException("手机号不存在")
                }
            }


            return loadedUser
        } catch (var4: UsernameNotFoundException) {
            throw var4
        } catch (var5: InternalAuthenticationServiceException) {
            throw var5
        } catch (var6: Exception) {
            throw InternalAuthenticationServiceException(var6.message, var6)
        }

    }

    companion object {
        private const val USER_NOT_FOUND_PASSWORD = "userNotFoundPassword"
    }
}
